f426e2d08db976900dfc91852484298364bcf356,src/dorkbox/systemTray/SystemTray.java,SystemTray,init,#boolean#,247
Before Change
FORCE_TRAY_TYPE = TrayType.AutoDetect;
}
}
else if (OS.isLinux()) {
// kablooie if SWT/JavaFX is not configured in a way that works with us.
if (FORCE_TRAY_TYPE != TrayType.Swing) {
if (isSwtLoaded) {
After Change
FORCE_TRAY_TYPE = TrayType.AutoDetect;
}
}
else if (OS.isLinux() || OS.isUnix()) {
// kablooie if SWT/JavaFX is not configured in a way that works with us.
if (FORCE_TRAY_TYPE != TrayType.Swing) {
if (isSwtLoaded) {
// Necessary for us to work with SWT based on version info. We can try to set us to be compatible with whatever it is set to
// System.setProperty("SWT_GTK3", "0");
// was SWT forced?
String swt_gtk3 = System.getProperty("SWT_GTK3");
boolean isSwt_GTK3 = swt_gtk3 != null && !swt_gtk3.equals("0");
if (!isSwt_GTK3) {
// check a different property
String property = System.getProperty("org.eclipse.swt.internal.gtk.version");
isSwt_GTK3 = property != null && !property.startsWith("2.");
}
if (isSwt_GTK3 && FORCE_GTK2) {
logger.error("Unable to use the SystemTray when SWT is configured to use GTK3 and the SystemTray is configured to use " +
"GTK2. Please configure SWT to use GTK2, via `System.setProperty(\"SWT_GTK3\", \"0\");` before SWT is " +
"initialized, or set `SystemTray.FORCE_GTK2=false;`");
systemTrayMenu = null;
systemTray = null;
return;
} else if (!isSwt_GTK3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) {
// we must use GTK2, because SWT is GTK2
logger.warn("Forcing GTK2 because SWT is GTK2");
FORCE_GTK2 = true;
}
}
else if (isJavaFxLoaded) {
// JavaFX Java7,8 is GTK2 only. Java9 can MAYBE have it be GTK3 if `-Djdk.gtk.version=3` is specified
// see
// http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
// https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm
// from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+.
boolean isJFX_GTK3 = System.getProperty("jdk.gtk.version", "2").equals("3");
if (isJFX_GTK3 && FORCE_GTK2) {
// if we are java9, then we can change it -- otherwise we cannot.
if (OS.javaVersion == 9 && AUTO_FIX_INCONSISTENCIES) {
logger.warn("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is " +
"configured to use GTK2. Please configure JavaFX to use GTK2 (via `System.setProperty(\"jdk.gtk.version\", \"3\");`) " +
"before JavaFX is initialized, or set `SystemTray.FORCE_GTK2=false;` Undoing `FORCE_GTK2`.");
FORCE_GTK2 = false;
} else {
logger.error("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is configured to use " +
"GTK2. Please set `SystemTray.FORCE_GTK2=false;` if that is not possible then it will not work.");
systemTrayMenu = null;
systemTray = null;
return;
}
} else if (!isJFX_GTK3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) {
// we must use GTK2, because JavaFX is GTK2
logger.warn("Forcing GTK2 because JavaFX is GTK2");
FORCE_GTK2 = true;
}
}
}
}
Class<? extends Tray> trayType = null;
if (DEBUG) {
logger.debug("OS: {}", System.getProperty("os.name"));
logger.debug("Arch: {}", System.getProperty("os.arch"));
String jvmName = System.getProperty("java.vm.name", "");
String jvmVersion = System.getProperty("java.version", "");
String jvmVendor = System.getProperty("java.vm.specification.vendor", "");
logger.debug("{} {} {}", jvmVendor, jvmName, jvmVersion);
logger.debug("Is AutoTraySize? {}", AUTO_TRAY_SIZE);
logger.debug("Is JavaFX detected? {}", isJavaFxLoaded);
logger.debug("Is SWT detected? {}", isSwtLoaded);
logger.debug("Is using native menus? {}", useNativeMenus);
logger.debug("Forced tray type: {}", FORCE_TRAY_TYPE.name());
logger.debug("FORCE_GTK2: {}", FORCE_GTK2);
}
// Note: AppIndicators DO NOT support tooltips. We could try to create one, by creating a GTK widget and attaching it on
// mouseover or something, but I don't know how to do that. It seems that tooltips for app-indicators are a custom job, as
// all examined ones sometimes have it (and it's more than just text), or they don't have it at all. There is no mouse-over event.
// this has to happen BEFORE any sort of swing system tray stuff is accessed
if (OS.isWindows()) {
// windows is funky, and is hardcoded to 16x16. We fix that.
SystemTrayFixes.fixWindows();
}
else if (OS.isMacOsX() && useNativeMenus) {
// macosx doesn't respond to all buttons (but should)
SystemTrayFixes.fixMacOS();
}
else if ((OS.isLinux() || OS.isUnix()) && FORCE_TRAY_TYPE != TrayType.Swing) {
// see: https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running
// For funsies, SyncThing did a LOT of work on compatibility (unfortunate for us) in python.
// https://github.com/syncthing/syncthing-gtk/blob/b7a3bc00e3bb6d62365ae62b5395370f3dcc7f55/syncthing_gtk/statusicon.py
// this can never be swing
// don't check for SWING type at this spot, it is done elsewhere.
if (SystemTray.FORCE_TRAY_TYPE != TrayType.AutoDetect) {
trayType = selectTypeQuietly(useNativeMenus, SystemTray.FORCE_TRAY_TYPE);
}
// quick check, because we know that unity uses app-indicator. Maybe REALLY old versions do not. We support 14.04 LTE at least
// if we are running as ROOT, we *** WILL NOT *** have access to 'XDG_CURRENT_DESKTOP'
// *unless env's are preserved, but they are not guaranteed to be
// see: http://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running
String XDG = System.getenv("XDG_CURRENT_DESKTOP");
if (XDG == null) {
// maybe we are running as root???
XDG = "unknown"; // try to autodetect if we should use app indicator or gtkstatusicon
}
// BLEH. if gnome-shell is running, IT'S REALLY GNOME!
// we must ALWAYS do this check!!
boolean isReallyGnome = OSUtil.DesktopEnv.isGnome();
if (isReallyGnome) {
if (DEBUG) {
logger.error("Auto-detected that gnome-shell is running");
}
XDG = "gnome";
}
if (DEBUG) {
logger.debug("Currently using the '{}' desktop", XDG);
}
if (trayType == null) {
// Unity is a weird combination. It's "Gnome", but it's not "Gnome Shell".
if ("unity".equalsIgnoreCase(XDG)) {
trayType = selectTypeQuietly(useNativeMenus, TrayType.AppIndicator);
}
else if ("xfce".equalsIgnoreCase(XDG)) {
// NOTE: XFCE used to use appindicator3, which DOES NOT support images in the menu. This change was reverted.
// see: https://ask.fedoraproject.org/en/question/23116/how-to-fix-missing-icons-in-program-menus-and-context-menus/
// see: https://git.gnome.org/browse/gtk+/commit/?id=627a03683f5f41efbfc86cc0f10e1b7c11e9bb25
// so far, it is OK to use GtkStatusIcon on XFCE <-> XFCE4 inclusive
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
}
else if ("lxde".equalsIgnoreCase(XDG)) {
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
}
else if ("kde".equalsIgnoreCase(XDG)) {
if (OSUtil.Linux.isFedora()) {
// Fedora KDE requires GtkStatusIcon
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
} else {
// kde (at least, plasma 5.5.6) requires appindicator
trayType = selectTypeQuietly(useNativeMenus, TrayType.AppIndicator);
}
// kde 5.8+ is "high DPI", so we need to adjust the scale. Image resize will do that
}
else if ("pantheon".equalsIgnoreCase(XDG)) {
// elementaryOS. It only supports appindicator (not gtkstatusicon)
// http://bazaar.launchpad.net/~wingpanel-devs/wingpanel/trunk/view/head:/sample/SampleIndicator.vala
if (!useNativeMenus && AUTO_FIX_INCONSISTENCIES) {
logger.warn("Cannot use non-native menus with pantheon DE. Forcing native menus.");
useNativeMenus = true;
}
// ElementaryOS shows the checkbox on the right, everyone else is on the left.
// With eOS, we CANNOT show the spacer image. It does not work
trayType = selectTypeQuietly(useNativeMenus, TrayType.AppIndicator);
}
else if ("gnome".equalsIgnoreCase(XDG)) {
// check other DE
String GDM = System.getenv("GDMSESSION");
if (DEBUG) {
logger.debug("Currently using the '{}' session type", GDM);
}
if ("gnome".equalsIgnoreCase(GDM)) {
if (OSUtil.Linux.isArch()) {
if (DEBUG) {
logger.debug("Running Arch Linux.");
}
if (!Extension.isInstalled()) {
logger.info("You may need a work-around for showing the SystemTray icon - we suggest installing the " +
"the [Top Icons] plugin (https://extensions.gnome.org/extension/1031/topicons/) which moves " +
"icons from the *notification drawer* (it is normally collapsed) at the bottom left corner " +
"of the screen to the menu panel next to the clock.");
}
} else {
// Automatically install the extension for everyone except Arch. It's bonkers.
Extension.install();
}
// are we fedora? If so, what version?
// now, what VERSION of fedora? 23/24/25/? don't have AppIndicator installed, so we have to use GtkStatusIcon
if (OSUtil.Linux.isFedora()) {
if (DEBUG) {
logger.debug("Running Fedora");
}
// 23 is gtk, 24/25 is gtk (but also wrong size unless we adjust it. ImageUtil automatically does this)
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
}
else if (OSUtil.Linux.isUbuntu()) {
// so far, because of the interaction between gnome3 + ubuntu, the GtkStatusIcon miraculously works.
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
}
else if (OSUtil.Unix.isFreeBSD()) {
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
}
else {
// arch likely will have problems unless the correct/appropriate libraries are installed.
trayType = selectTypeQuietly(useNativeMenus, TrayType.AppIndicator);
}
}
else if ("cinnamon".equalsIgnoreCase(GDM)) {
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
}
else if ("gnome-classic".equalsIgnoreCase(GDM)) {
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
}
else if ("gnome-fallback".equalsIgnoreCase(GDM)) {
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
}
else if ("ubuntu".equalsIgnoreCase(GDM)) {
trayType = selectTypeQuietly(useNativeMenus, TrayType.AppIndicator);
}
}
}
// Try to autodetect if we can use app indicators (or if we need to fallback to GTK indicators)
if (trayType == null) {
BufferedReader bin = null;
try {
// the ONLY guaranteed way to determine if indicator-application-service is running (and thus, using app-indicator),
// is to look through all /proc/<pid>/status, and first line should be Name:\tindicator-appli
File proc = new File("/proc");
File[] listFiles = proc.listFiles();
if (listFiles != null) {
for (File procs : listFiles) {
String name = procs.getName();
if (!Character.isDigit(name.charAt(0))) {
continue;
}
File status = new File(procs, "status");
if (!status.canRead()) {
continue;
}
try {
bin = new BufferedReader(new FileReader(status));
String readLine = bin.readLine();
if (readLine != null && readLine.contains("indicator-app")) {
// make sure we can also load the library (it might be the wrong version)
try {
trayType = selectType(useNativeMenus, TrayType.AppIndicator);
} catch (Exception e) {
if (DEBUG) {
logger.error("AppIndicator support detected, but unable to load the library. Falling back to GTK", e);
} else {
logger.error("AppIndicator support detected, but unable to load the library. Falling back to GTK");
}
}
break;
}
} finally {
IO.closeQuietly(bin);
}
}
}
} catch (Throwable e) {
if (DEBUG) {
logger.error("Error detecting gnome version", e);
}
}
}
// fallback...
if (trayType == null) {
trayType = selectTypeQuietly(useNativeMenus, TrayType.GtkStatusIcon);
logger.warn("Unable to determine the system window manager type. Falling back to GtkStatusIcon.");
}
// this is bad...
if (trayType == null) {
logger.error("SystemTray initialization failed. Unable to load the system tray native libraries. Please write an issue " +
"and include your OS type and configuration");
systemTrayMenu = null;
systemTray = null;
return;
}
if (isTrayType(trayType, TrayType.AppIndicator)) {
// if are we running as ROOT, there can be issues (definitely on Ubuntu 16.04, maybe others)!
// this means we are running as sudo
String sudoUser = System.getenv("SUDO_USER");
if (sudoUser != null) {
// running as a "sudo" user
logger.error("Attempting to load the SystemTray as the 'root' user. This will likely not work because of dbus restrictions.");
}
else {
// running as root (also can be "sudo" user). A bit slower that checking a sys env, but this is guaranteed to work
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
PrintStream outputStream = new PrintStream(byteArrayOutputStream);
// id -u
final ShellProcessBuilder shell = new ShellProcessBuilder(outputStream);
shell.setExecutable("id");
shell.addArgument("-u");
shell.start();
String output = ShellProcessBuilder.getOutput(byteArrayOutputStream);
if ("0".equals(output)) {
logger.error("Attempting to load the SystemTray as the 'root' user. This will likely not work because of dbus " +
"restrictions.");
}
} catch (Throwable e) {
if (DEBUG) {
logger.error("Cannot get id for root", e);
}
}
}
}
}
// this is likely windows OR mac
if (trayType == null) {
try {
trayType = selectType(useNativeMenus, TrayType.Swing);
} catch (Throwable e) {
if (DEBUG) {
logger.error("Maybe you should grant the AWTPermission `accessSystemTray` in the SecurityManager.", e);
} else {
logger.error("Maybe you should grant the AWTPermission `accessSystemTray` in the SecurityManager.");
}
}
}
if (trayType == null) {
// unsupported tray, or unknown type
logger.error("SystemTray initialization failed. (Unable to discover which implementation to use). Something is seriously wrong.");
systemTrayMenu = null;
systemTray = null;
return;
}
ImageUtils.determineIconSize();
final AtomicReference<Tray> reference = new AtomicReference<Tray>();
// - appIndicator/gtk require strings (which is the path)
// - swing version loads as an image (which can be stream or path, we use path)
CacheUtil.tempDir = "SysTray";
try {
if (OS.isLinux() || OS.isUnix()) {
// NOTE: appindicator1 -> GTk2, appindicator3 -> GTK3.
// appindicator3 doesn't support menu icons via GTK2!!
if (!Gtk.isLoaded) {